
const char version[] = "v3.9";
#include<Windows.h>
#include <thread>
#include <atomic>
#include "wmi64.h"

#pragma comment(lib, "Kernel32.lib")
#pragma comment(lib,"User32.lib")

#define USING_EASYX 1

#include <stdio.h>
#if USING_EASYX
//#include<graphics.h>
    #ifdef UNICODE
        #undef UNICODE
    #endif
    #ifdef _UNICODE
        #undef _UNICODE
    #endif
    #include <easyx.h>
//#pragma comment(lib, "Gdi32.lib")
    #ifdef UNICODE
	    #pragma comment(lib,"EasyXw.lib")
    #else
	    #pragma comment(lib,"EasyXa.lib")
    #endif
    
#include <ctime>
//#else
//#include <conio.h>
#endif

#define systemmonitorAPI __stdcall
#define systemmonitorTHREADAPI void
#define KEYDOWN_S(VK) ((GetAsyncKeyState(VK)& 0x8000 && GetForegroundWindow() == wMain))

#pragma comment(lib, "shell32.lib")
#define MOVEPXL_LARGE 180
#define MOVEPXL_S_MALL 10
#define WIDTH 136
#define HEIGHT 136
#define LENGTH 136
#define XSTART 800
#define YSTART 450

#define PI 3.1415926535898
HWND wMain;
int x, y;


const char* KeyHelper = "WASD: Move window slowly.\nDIRECT: Move window fastly.\nB/M: Adjust font weight.\n\
U/L: Adjust Alpha value of window.\nESC: Quit/Exit this program.";

#if USING_EASYX
void Move_Window_Main(int xadd, int yadd);
int mid(int);
int mid(double);
#endif
__int64 Filetime2Int64(const FILETIME &ftime);
__int64 CompareFileTime2(const FILETIME &preTime, const FILETIME &nowTime);
double getCpuUsage();
void xyprintf(TCHAR[], int, int, int);
int wcslent(TCHAR[]);

std::atomic<double> CPU_Get_Now = 0.0001;
std::atomic<long> Memory_Get_Now = 1;
bool End_FP = false;

LONG_PTR trtl = 0;
TCHAR trtl_res[50]; // result
time_t LastGetTrulyRunTime = time(nullptr);
constexpr int await_t = 369;
constexpr int await_tplus = await_t + 69;
inline void SetTrulyRunTime()
{
    time_t tn = time(NULL);
    LONG64 dftime = static_cast<long long>(difftime(tn, LastGetTrulyRunTime));
    if (dftime < await_tplus)
    {
        trtl += dftime;
        LastGetTrulyRunTime = tn;
    }
    else
    {
        LastGetTrulyRunTime = tn;
    }
}
inline TCHAR* GetTrulyRunTimeLong()
{
    long long More = static_cast<long long>(trtl);
    int d, h, m, s;
    d = static_cast<int>( More / (24 * 60 * 60));
    More -= d * 24 * 3600;
    h = (int)(More / (3600));
    More -= h * 3600;
    m = (int)(More / 60);
    More -= m * 60;
    s = (int)(More);
    _stprintf((char*)trtl_res, "%d+%d:%d:%d", d, h, m, s);
    return trtl_res;
}
SMTHREADAPI RunTimeForEachMin()
{
    while (End_FP == false)
    {
        SetTrulyRunTime();
        Sleep(await_t);
    }
}

#if USING_EASYX
namespace snamed {
    // systemmonitor draw
    void fill(COLORREF c);
    void rect(int x, int y, int width, int height, COLORREF fg, COLORREF bg, bool NoBackground);
    void ring(int x, int y, int radius, COLORREF fg, COLORREF bg, bool NoBackground);
    void apie(int x, int y, int D, double percent);
}
#endif

#ifdef UNICODE
    #define MsgBox(A, C) (MessageBoxW(NULL, A, C, MB_OK))
#else
    #define MsgBox(A, C) (MessageBoxA(NULL, A, C, MB_OK))
    #endif

HMODULE dll;
typedef double(*GETCPU)();
typedef long(*GETMEMORY)();
GETCPU GETCPU_P;
GETMEMORY GETMEMORY_P;

void WINAPI StartGetCpuF()
{
    GETCPU_P = (GETCPU)GetProcAddress(dll, "GetCpuUsage");
    GETMEMORY_P = (GETMEMORY)GetProcAddress(dll, "GetMemoryUsage");
    //MsgBox(_T("Start cpu thread."), _T("System Monitor"));
    while (End_FP == false)
    {
        CPU_Get_Now = GETCPU_P() * 100;
        Memory_Get_Now = GETMEMORY_P();
        Sleep(2080);
    }
    //MsgBox(_T("Stop render thread."), _T("System Monitor"));
    return;
}

int __stdcall _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow)
{
    #if USING_EASYX
    wMain = initgraph(WIDTH, HEIGHT, EX_NOCLOSE);
    MoveWindow(wMain, 1500, 520, WIDTH, HEIGHT, true);
    SetWindowPos(wMain, HWND_TOPMOST, 1500, 520, WIDTH, HEIGHT, SWP_NOMOVE | SWP_NOSIZE);
    SetWindowLong(wMain, GWL_STYLE, GetWindowLong(wMain, GWL_STYLE) & ~WS_CAPTION);
    SetWindowLong(wMain, GWL_EXSTYLE, GetWindowLong(wMain, GWL_EXSTYLE) & (~(WS_EX_WINDOWEDGE | WS_EX_DLGMODALFRAME)) | WS_EX_LAYERED | WS_EX_TOOLWINDOW);

    int Alpha_win = 128;
    SetLayeredWindowAttributes(wMain, 0, 128, LWA_ALPHA);
    #endif
    x = XSTART;
    y = YSTART;
    time_t T0 = time(nullptr);
    time_t Time_Now = T0;
    long More = 0.0;
    int d = 0, h = 0, m = 0, s = 0; // time
    double cpu_percent = 0.0;
    float mem_percent = 0.0;
    long mem_used = 0;

    int key_get;
    dll = LoadLibraryA("wmi64.dll");
    if (dll == NULL)
    {
        MessageBoxA(wMain, "Can not load dynamic link library 'wmi64.dll'.", "System Monitor", MB_OK | MB_ICONERROR);
        return 114514;
    }

    // Warning when memory usage >= 90
    bool warn_mem_on = false; // mem>=90 show
    //bool isRed = false;
    int Alert_Color_Index = 0;
    COLORREF AlertColors[4] = {RGB(255, 255, 255), RGB(255, 96, 0), RGB(255, 255, 255), RGB(32, 96, 255)};//??
    AlertColors[0] = RGB(255, 255, 255);
    AlertColors[1] = RGB(255, 96, 0);
    AlertColors[2] = RGB(255, 255, 255);
    AlertColors[3] = RGB(32, 96, 255);
    int ManualTest = 0;

    // eachminrun
    int emr = 10; //++ -> 10 -> 0
    TCHAR* trtl_Now_StrP;
    bool SYS_IFIfShow_Version = false;

    #ifdef UNICODE
    TCHAR cpu_text[24];
    TCHAR mem_text[24];
    TCHAR tim_text[24];
    TCHAR alp_text[8];
    wchar_t *ct;
    wchar_t *mt;
    wchar_t *tt;
    wchar_t *at;
    settextstyle(12, 7, _T("simhei"));
    #else
    char cpu_text[36];
    char mem_text[36];
    char tim_text[36];
    char alp_text[18];
    //settextstyle(12, 7, (LPCTSTR)_T("Consolas"));
    #endif
    int FontHeight = 12;
    settextstyle(FontHeight, 0, (LPCTSTR)_T("simhei"));
    settextcolor(RGB(255, 0, 255));
    LOGFONT f;
    gettextstyle(&f);
    f.lfQuality = ANTIALIASED_QUALITY;
    //f.lfWeight = FW_SEMIBOLD;
    settextstyle(&f);
    setbkmode(TRANSPARENT);
    std::thread(StartGetCpuF).detach();
    std::thread(RunTimeForEachMin).detach();
    BeginBatchDraw();
    while (true)
    {
        Sleep(mem_used > 86 ? 69 : await_tplus);
        cpu_percent = CPU_Get_Now;
        mem_used = Memory_Get_Now;
        if (ManualTest > 0){
            ManualTest --;
            mem_used = 91;
        }
        if (mem_used > 86) {
            warn_mem_on = true;
            //isRed = isRed ? false : true;
            Alert_Color_Index ++;
            if (Alert_Color_Index > 3) {
                Alert_Color_Index = 0;
            }//Alert_Color_Index = 1;
            //Alpha_win = 248;
        } else {
            warn_mem_on = false;Alert_Color_Index = 0;
        }
        // draw
        #if USING_EASYX
        snamed::fill(AlertColors[Alert_Color_Index]);
        snamed::ring(LENGTH / 2, LENGTH / 2, LENGTH / 2, RGB(182, 255, 255), RGB(196, 255, 255), false);
        snamed::apie(0, 0, LENGTH, cpu_percent);
        snamed::ring(LENGTH / 2, LENGTH / 2, static_cast<int>((float)LENGTH * 0.618 * 0.500), RGB(160, 255, 255), RGB(172, 255, 255), false);
        snamed::apie(LENGTH * 0.382 * 0.500, LENGTH * 0.382 * 0.500, LENGTH * 0.618, (double)mem_used);

        #ifdef UNICODE // it has bugs!
        cpu_text[0] = L'\0';
        mem_text[0] = L'\0';
        ct = (wchar_t*)cpu_text;
        swprintf(ct, 23, L"CPU: = %.2f%% Mem:%ld%%", cpu_percent, mem_used);
        mt = (wchar_t *)mem_text;
        swprintf(mt, 23, L"Memory:%ld%%", mem_used);
        settextcolor(RGB(255, 0, 255));
        outtextxy(0, 0, cpu_text);
        settextcolor(RGB(0, 255, 0));
        outtextxy(0, HEIGHT - 16, mt);
        settextcolor(RGB(255, 0, 128));
        Time_Now = time(nullptr);
        Time_Now = static_cast<time_t>(difftime(Time_Now, T0));
        More = static_cast<long>(Time_Now);
        d = static_cast<int>( More / (24 * 60 * 60));
        More -= d * 24 * 3600;
        h = (int)(More / (3600));
        More -= h * 3600;
        m = (int)(More / 60);
        More -= m * 60;
        s = (int)(More);
        tt = (wchar_t *)tim_text;
        swprintf(tt, 23, L"%d+%d:%d:%d", d, h, m, s);
        //xyprintf(tim_text, 0, HEIGHT - 8, 12);
        outtextxy(0, HEIGHT - 9, tt);
        settextcolor(RGB(0, 255, 255));
        at = (wchar_t *)alp_text;
        swprintf(at, 7, L"%d", Alpha_win);
        outtextxy(WIDTH - 26, HEIGHT - 9, alp_text);
        #error ("Do not use utf-16!")
        #endif
        
        #ifndef UNICODE
        cpu_text[0] = '\0'; cpu_text[1] = '\0';
        mem_text[0] = '\0';mem_text[1] = '\0';
        sprintf(cpu_text, "CPU:%.1lf%%", cpu_percent);
        sprintf(mem_text, "Memory:%ld%%", mem_used);
        setbkmode(TRANSPARENT);
        settextcolor(RGB(255, 0, 255));
        outtextxy(0, 0, (LPCTSTR)cpu_text);
        settextcolor(RGB(30, 169, 10));
        outtextxy(0, HEIGHT - 1 - FontHeight - FontHeight, (LPCTSTR)mem_text);
        if (!SYS_IFIfShow_Version)
        {
            settextcolor(RGB(255, 128, 5));
            outtextxy(WIDTH - 28, HEIGHT - 1 - FontHeight - FontHeight, (LPCTSTR)version);
        }
        //settextcolor(RGB(255, 0, 255));
        settextcolor(RGB(69, 69, 200));
        //emr++;
        if (true || emr > 2) {
            emr = 0;
            Time_Now = time(nullptr);
            Time_Now = static_cast<time_t>(difftime(Time_Now, T0));
            More = static_cast<long>(Time_Now);
            d = static_cast<int>( More / (24 * 60 * 60));
            More -= d * 24 * 3600;
            h = (int)(More / (3600));
            More -= h * 3600;
            m = (int)(More / 60);
            More -= m * 60;
            s = (int)(More);
            sprintf(tim_text, "%d+%d:%d:%d", d, h, m, s);
            trtl_Now_StrP = GetTrulyRunTimeLong();
        }
        //outtextxy(0, HEIGHT - 10, (LPCTSTR)tim_text);
        outtextxy(WIDTH - textwidth((LPCTSTR)tim_text)-1, 0, (LPCTSTR)tim_text);
        settextcolor(RGB(0, 255, 255));
        sprintf(alp_text, "Ap=%d", Alpha_win);
        //outtextxy(WIDTH - 46, /*HEIGHT - 10*/ 0, (LPCTSTR)alp_text);
        outtextxy(0, HEIGHT - FontHeight, (LPCTSTR)alp_text);
        //settextcolor(RGB(170, 146, 20));
        settextcolor(RGB(20, 150, 0));
        outtextxy(WIDTH - textwidth(trtl_Now_StrP) - 1, HEIGHT - FontHeight - 1, trtl_Now_StrP);
        #endif
        //wprintf(tt);
        #else
        printf("cpu: %f, mem: %f, used: %ld\n", cpu_percent, mem_percent, mem_used);
        #endif
        //Sleep(2100);
        #if USING_EASYX
        //setbkmode(OPAQUE);
        if (KEYDOWN_S('A')) {
            Move_Window_Main(-MOVEPXL_S_MALL, 0);
        } else if (KEYDOWN_S('D')) 
        {
            Move_Window_Main(MOVEPXL_S_MALL, 0);
        }
        else if (KEYDOWN_S('W'))
        {
            Move_Window_Main(0, -MOVEPXL_S_MALL);
        }
        else if (KEYDOWN_S('S')) {
            Move_Window_Main(0, MOVEPXL_S_MALL);
        }
        else if (KEYDOWN_S(VK_UP))
        {
            Move_Window_Main(0, -MOVEPXL_LARGE);
        }
        else if (KEYDOWN_S(VK_DOWN))
        {
            Move_Window_Main(0, MOVEPXL_LARGE);
        }
        else if (KEYDOWN_S(VK_LEFT))
        {
            Move_Window_Main(-MOVEPXL_LARGE, 0);
        }
        else if (KEYDOWN_S(VK_RIGHT))
        {
            Move_Window_Main(MOVEPXL_LARGE, 0);
        }
        if KEYDOWN_S('U') {
            if (Alpha_win > 255-8) {
                ;
            }
            else {
                Alpha_win += 8;
                if (Alpha_win > 255){
                    Alpha_win = 255;
                }
                SetLayeredWindowAttributes(wMain, 0, Alpha_win, LWA_ALPHA);
            }
        }
        else if (KEYDOWN_S('L'))
        {
            if (Alpha_win < 8) {
                ;
            } else {
                Alpha_win -= 8;
                if (Alpha_win < 0){
                    Alpha_win = 0;
                }
                SetLayeredWindowAttributes(wMain, 0, Alpha_win, LWA_ALPHA);
            }
        }
        if (KEYDOWN_S('B'))
        {
            //Bigger font
            FontHeight++;
            settextstyle(FontHeight, int((double)FontHeight / 12 * 7), (LPCTSTR)_T("Consolas"));
        }
        else if (KEYDOWN_S('M'))
        {
            //  S_MALLer font
            if (FontHeight > 3)
            {
                FontHeight --;
                settextstyle(FontHeight, int((double)FontHeight / 12 * 7), (LPCTSTR)_T("simhei"));
            }
            else
            {
                MessageBoxA(wMain, "Font weight can not less than 3.", "System Monitor", MB_OK | MB_ICONSTOP);
            }
        }
        if (KEYDOWN_S('T')) {
            ManualTest = 16;
        }
        if (KEYDOWN_S('V'))
        {
            SYS_IFIfShow_Version = !SYS_IFIfShow_Version;
        }
        if (KEYDOWN_S('H'))
        {
            MessageBoxA(NULL, KeyHelper, "System", MB_OK);
        }
        if KEYDOWN_S(VK_ESCAPE) {
            End_FP = true;
            return 0;
        }
        FlushBatchDraw();
        
        #else
        //if (kbhit()){
        //    break;
        //}
        ;
        #endif
    }
    #if USING_EASYX
    settextcolor(RGB(0, 0, 64));
    outtextxy(8, HEIGHT / 2, (LPCTSTR)_T("Exit program..."));
    EndBatchDraw();
    Sleep(1023);
    closegraph();
    #endif
    return 0;
}

#if USING_EASYX
void Move_Window_Main(int xadd, int yadd)
{
    RECT rect;
    GetWindowRect(wMain, &rect);
    int x1, y1;
    x1 = rect.left + xadd;
    y1 = rect.top + yadd;
    if (x1 > 1690 )
    {
        x1 = 1690;
    }else if(x1 < 0){
        x1 = 0;
    }
    if (y1 > 985){
        y1 = 985;
    } else if (y1 < 0) {
        y1 = 0;
    }
    MoveWindow(wMain, x1, y1, WIDTH, HEIGHT, true);
}

void snamed::fill(COLORREF c)
{
    setfillcolor(c);
    fillrectangle(0, 0, WIDTH+50, HEIGHT+50);
}

void snamed::rect(int x, int y, int width, int height, COLORREF fg, COLORREF bg = RGB(255, 255, 255), bool NoBackground = true)
{
    ;
}

void snamed::ring(int x, int y, int radius, COLORREF fg, COLORREF bg = NULL, bool NoBackground = true)
{
    if (bg != NULL && NoBackground == false) setfillcolor(bg);
    setlinecolor(fg);
    if (NoBackground == false) fillcircle(x, y, radius);
    else circle(x, y, radius);
}

void snamed::apie(int x, int y, int D,
    double percent // 0 ~ 100.0
)
{
    if (percent < 0.1) {
        return; // draw nil
    } else
    if (percent < 0.5) {
        percent = 0.5;
    } else if (percent > 99.2) {
        percent = 99.2;
    }
    double end_agl = 0.0;
    const double per = percent;
    int red = 0, green = 255, blue = 255;
    end_agl = per * 2 * PI / 100;
    if (per <= 33.333333){
        red = 0;
        green = 255;
        blue = (255 - mid( static_cast<int>(per * 3 * 255 / 100) ) );
    }
    else if (per <= 66.666667){
        red = mid((per - 33.333333) * 3 * 255 / 100);
        green = 255;
        blue = 0;
    }
    else {
        red = 255;
        green = 255 - mid(static_cast<int>((per - 66.666667) * 3 * 255 / 100));
        blue = 0;
    }
    setfillcolor(RGB(red, green, blue));
    fillpie(x, y, x + D, y + D, 0, end_agl);
}

int mid(int c)
{
    if(c > 255){
        return 255;
    }
    else if (c < 0){
        return 0;
    }
    return c;
}

int mid(double f)
{
    int c = 0;
    c = static_cast<int>(f);
    return mid(c);
}
#endif
